package org.rainwalk.bill.api.advice;

import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.json.JSONUtil;
import org.rainwalk.bill.api.model.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.PostConstruct;
import java.nio.charset.Charset;

@ControllerAdvice
@ConditionalOnProperty(prefix = "app.crypto", name = "enable", havingValue = "true")
public class AppResponseBodyAdvice extends AbstractAppCrypto implements ResponseBodyAdvice<Object> {

    @Value("${app.crypto.enable}")
    private boolean isCrypto;

    @Value("${app.crypto.public-key}")
    private String publicKey;

    private RSA rsa;

    @PostConstruct
    public void init() {
        rsa = new RSA(AsymmetricAlgorithm.RSA.getValue(), null, publicKey);
    }


    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        Object body;
        if (result == null) {
            body = Response.success();
        } else if (result instanceof Response) {
            body = result;
        }
        else if (result instanceof String) {
            body = Response.success(result);
        } else {
            body =  Response.success(result);
        }
        if (!isCrypto || !isNeed(methodParameter)) {
            if (methodParameter.getMethod().getGenericReturnType().equals(String.class)) {
                return JSONUtil.toJsonStr(body);
            }
            return body;
        }
        String originBody = JSONUtil.toJsonStr(body);
        return rsa.encryptBase64(originBody, Charset.forName(charset), KeyType.PublicKey);
    }

}
